package cn.jjxx.core.utils.upload;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.coobird.thumbnailator.Thumbnails;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.framework.customutil.DateUtils;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import cn.jjxx.core.utils.CacheUtils;
import cn.jjxx.core.utils.ObjectUtils;
import cn.jjxx.core.utils.ServletUtils;
import cn.jjxx.core.utils.StringUtils;

/**
 * @Title: WebuploaderUtils.java .<br>
 * @Package cn.jjxx.core.utils.upload .<br>
 * @Description: 大文件上传、分块上传、断点续传功能 .<br>
 * @author 郑成功 .<br>
 * @email a876459952@qq.com .<br>
 * @date 2018-12-25 上午9:59:04.<br>
 * @version V1.0.<br>
 */
public class WebuploaderUtils extends FileUploadUtils{
	
	public static final String FILE_UPLOAD_CACHE_NAME = "upload";//存放文件的缓存名称
	
	public static final String FILEMD5 = "fileMd5_";//MD5标识缓存前缀
	
	public static final String LAST_UPLOAD_TIME = "lastUploadTime_";//最后上传时间缓存前缀
	
	public static final String PROGRESS_RATE = "progressRate_";//进度缓存前缀
	
	/**
	 * @Description: 合并分块文件 .<br>
	 * @param basePath 基础路径.<br>
	 * @param fileMd5 文件的MD5值.<br>
	 * @param fileHash 文件的hash值.<br>
	 * @param fileName 文件名称.<br>
	 * @param params 其他参数.<br>
	 * @throws Exception 抛出异常信息.<br>   
	 * @author 郑成功 .<br>
	 * @date 2018-12-25 上午9:59:45.<br>
	 */
	@SuppressWarnings("resource")
	public static String MergeFileBlock(String basePath,String fileMd5,String fileHash, String fileName,Map<String,Object> params) throws Exception{
		//读取分片文件的目录
		File f = new File(basePath + SEPARATY_SIGN + CacheUtils.get(FILE_UPLOAD_CACHE_NAME, FILEMD5 + fileHash));
		//获取当前文件夹下的文件，并转成集合排序
		File[] fileArray = FileUtils.listFiles(f, FileFilterUtils.suffixFileFilter(""), null).toArray(new File[0]);
		//File[] fileArray = f.listFiles();
		List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));
		Collections.sort(fileList, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
                    return -1;
                }
                return 1;
            }
        });
		System.out.println(fileName);
		return MergeFile(basePath, fileHash, fileName, fileList, f);
	}
	
	/**
	 * @Description: 合并文件 .<br>
	 * @param basePath 基础路径.<br>
	 * @param fileHash 文件hash标识.<br>
	 * @param fileName 文件名称.<br>
	 * @param fileList 文件的file集合.<br>
	 * @param f 临时文件夹.<br>
	 * @throws IOException .<br>   
	 * @author 郑成功 .<br>
	 * @date 2018-12-28 上午9:56:28.<br>
	 */
	@SuppressWarnings("resource")
	private static synchronized String MergeFile(String basePath,String fileHash,String fileName,List<File> fileList,File f) throws IOException{
		String filePath = null;
		//TODO 线程
		//在当前临时文件夹下,创建一个合并的临时文件
		File outputFile = new File(basePath + CacheUtils.get(FILE_UPLOAD_CACHE_NAME, FILEMD5 + fileHash)+SEPARATY_SIGN+fileName);
		outputFile.createNewFile();
		FileChannel outChannel = new FileOutputStream(outputFile).getChannel();
		//合并文件
		FileChannel inChannel;
        for (File file : fileList) {
            inChannel = new FileInputStream(file).getChannel();
            inChannel.transferTo(0, inChannel.size(), outChannel);
            inChannel.close();
            // 删除分片
            file.delete();
        }
        outChannel.close();
		//将该文件存放到相关目录下，生成指定文件
    	filePath = upload(outputFile);
        //删除文件夹
        deleteFile(f);
        //清除文件缓存信息（进度、MD5标识、最后更新时间）
        CacheUtils.remove(FILE_UPLOAD_CACHE_NAME, LAST_UPLOAD_TIME + fileHash);
        CacheUtils.remove(FILE_UPLOAD_CACHE_NAME, FILEMD5 + fileHash);
        CacheUtils.remove(FILE_UPLOAD_CACHE_NAME, PROGRESS_RATE + fileHash);
        return filePath;
	}
	
	/**
	 * @Description: 检测分块文件是否上传，并标记上传文件分块 .<br>
	 * @param progressRate 上传进度.<br>
	 * @param fileMd5 上传文件的MD5标志.<br>
	 * @param chunk 分块.<br>
	 * @param chunkSize 块大小.<br>
	 * @param params 其他参数.<br>
	 * @author 郑成功 .<br>
	 * @date 2018-12-24 下午3:43:38.<br>
	 */
	public static boolean uploadFileBlock(String basePath,String fileHash,String progressRate,String fileMd5,String chunk,Integer chunkSize,Map<String,Object> params){
        //获取当前上传的时间
        String lastUploadTime = DateUtils.date2Str(DateUtils.yyyymmddhhmmss);
        // 将最后上传时间以字符串形式存入缓存
        CacheUtils.put(FILE_UPLOAD_CACHE_NAME, LAST_UPLOAD_TIME + fileHash, lastUploadTime);
        // 将当前进度存入缓存
        CacheUtils.put(FILE_UPLOAD_CACHE_NAME, PROGRESS_RATE + fileHash, progressRate);
        //将文件对应的hash值存入,为分块提供存放的文件路径
        //判断文件夹是否在缓存里面,不存在则创建
        if(ObjectUtils.isNullOrEmpty(CacheUtils.get(FILE_UPLOAD_CACHE_NAME, FILEMD5 + fileHash))){
        	CacheUtils.put(FILE_UPLOAD_CACHE_NAME, FILEMD5 + fileHash, fileMd5);
        }
        String filePath = basePath+SEPARATY_SIGN+CacheUtils.get(FILE_UPLOAD_CACHE_NAME, FILEMD5 + fileHash)+SEPARATY_SIGN+chunk;
        File checkFile = new File(filePath);
        if(checkFile.exists() && checkFile.length() == chunkSize){
        	return true;
        }
		return false;
	}
	
	public void fileSave(HttpServletRequest request, HttpServletResponse response) {
		
	}
	
	/**
	 * @Description: 上传文件 .<br>
	 * @param file 需要上传的文件.<br>
	 * @throws IOException .<br>   
	 * @author 郑成功 .<br>
	 * @date 2018-12-27 下午1:48:56.<br>
	 */
	protected static String upload(File file) throws IOException{
		//获取保存当前文件基本
		HttpServletRequest request = ServletUtils.getRequest();
		String filePath = extractUploadDir(request);
		//获取默认上传地址
		String baseDir = getDefaultBaseDir();
		String filename = file.getName();
		filename = baseDir + SEPARATY_SIGN + datePath() + SEPARATY_SIGN + StringUtils.randomUUID() + "."
				+ StringUtils.getExtensionName(filename);
		File desc = getAbsoluteFile(filePath, filename);
		if(START_REMOTE){
			final String uploadUrl = REMOTE_UPLOAD_URL;
			uploadFileService(desc, uploadUrl);
		}
		if(START_LOCAL){
			//判断是否
			if(START_IMG_PRESS&&Arrays.asList(FileUploadUtils.IMAGE_EXTENSION).contains(StringUtils.getExtensionName(filename))){
				pressImg(file, filename, desc, filePath);
			}else{
				String src = file.getAbsolutePath();
				String dest = desc.getAbsolutePath();
				org.framework.superutil.j2se.FileUtils.copyFile(src, dest, 2048);
			}
		}
		return filename;
	}
	
	/**
	 * @Description: 压缩图片 .<br>
	 * @param file 源文件.<br>
	 * @param filename 文件名称.<br>
	 * @param desc 压缩文件.<br>
	 * @param filePath 文件路径.<br>
	 * @throws FileNotFoundException .<br>
	 * @throws IOException .<br>   
	 * @author 郑成功 .<br>
	 * @date 2018-12-27 下午4:59:52.<br>
	 */
	private static void pressImg(File file,final String filename, final File desc, final String filePath) throws FileNotFoundException, IOException{
		Thumbnails.of(new FileInputStream(file)).scale(1f).outputQuality(0.2f).toFile(desc);	
		new Thread(new Runnable() {
	        @Override
	        public void run() {
	        	try {
					//存储小图
					String minBaseDir = filename.substring(0,filename.lastIndexOf("/")+1);
					String extend = filename.substring(filename.lastIndexOf("."));
					String minFileName = filename.substring(filename.lastIndexOf("/")+1,filename.lastIndexOf("."))+FileUploadUtils.MIN_FILE+extend;
					File minFile = getAbsoluteFile(filePath, minBaseDir+minFileName);
					Thumbnails.of(new FileInputStream(desc)).scale(0.1f).outputQuality(0.2f).toFile(minFile);
				} catch (Exception e) {
					e.printStackTrace();
				}
	        }
	    }).start();
	}
	
	/**
	 * @Description: 删除当前文件夹及以下文件 .<br>
	 * @param file 当前文件夹文件.<br>   
	 * @author 郑成功 .<br>
	 * @date 2018-12-27 上午11:13:02.<br>
	 */
	protected static void deleteFile(File file){
		File[] files = file.listFiles();
        for(File f:files){
        	if(f.isFile()){
        		f.delete();
        	}
        }
        file.delete();
	}

}
